package com.wans.guli.search.server.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wans.guli.search.config.ElasticSearchConfig;
import com.wans.guli.search.entity.SkuClientEntity;
import com.wans.guli.search.entity.SkuEntity;
import com.wans.guli.search.server.ProductService;
import com.wans.guli.search.vo.ProductSearchParamVO;
import com.wans.guli.search.vo.ProductSearchRespVO;
import com.wans.guli.search.vo.SkuEsRespVO;
import com.wans.guli.search.vo.SkuEsRestVO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.dromara.easyes.core.toolkit.FieldUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service("ProductByESClientServiceImpl")
@Slf4j
/*
   通过ESClient保存商品信息
 */
public class ProductByESClientServiceImpl implements ProductService {

    @Autowired
    private RestHighLevelClient esClient;
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 保存商品信息
     *
     * @param skuEsRestVOList 商品信息
     */
    @Override
    public void save(List<SkuEsRestVO> skuEsRestVOList) {
        // 属性拷贝到SkuEntity
        List<SkuClientEntity> skuClientEntities = BeanUtil.copyToList(skuEsRestVOList, SkuClientEntity.class);

        // 创建批量请求
        BulkRequest br = new BulkRequest();
        for (SkuClientEntity skuClientEntity : skuClientEntities) {
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.index("product")
                    .id(String.valueOf(skuClientEntity.getSkuId()))
                    .source(skuClientEntity);
            br.add(indexRequest);
        }


        try {
            // 批量插入数据
            BulkResponse result = esClient.bulk(br, ElasticSearchConfig.COMMON_OPTIONS);

            log.info("批量保存数据结果：{}", result.status());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 根据参数查询商品信息
     *
     * @param productSearchParamVO 参数
     * @return 商品信息
     */
    @Override
    @SneakyThrows
    public ProductSearchRespVO getSkuInfoBySearchParam(ProductSearchParamVO productSearchParamVO) {

        // 构建查询 DSL
        SearchSourceBuilder sourceBuilder = getSkuInfoBySearchDSL(productSearchParamVO);

        // 创建查询
        SearchRequest searchRequest = new SearchRequest("product");
        searchRequest.source(sourceBuilder);

        // 执行查询
        SearchResponse response = esClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);

        ProductSearchRespVO productSearchResp = getProductSearchResp(response);
        // 设置分页
        productSearchResp.setPageNum(productSearchParamVO.getPageNum());
        productSearchResp.setTotalPages(Math.addExact(Math.floorDiv(productSearchResp.getTotal().intValue(), 2), 1));


        log.error("查询结果：{}", productSearchResp);

        // 设置分页
        ArrayList<Integer> list = new ArrayList<>();
        for (Integer integer = 1; integer < productSearchResp.getTotalPages(); integer++) {
            list.add(integer);
        }
        productSearchResp.setPageNavs(list);
        // 设置导航
        List<ProductSearchRespVO.NavVo> navs = new ArrayList<>();
        if (productSearchResp.getAttrs() != null && !productSearchResp.getAttrs().isEmpty()) {
            productSearchResp.getAttrs().forEach(attr -> {
                attr.getAttrValue().forEach(attrValue -> {
                    if (productSearchParamVO.getQueryString() != null) {
                        ProductSearchRespVO.NavVo navVo = new ProductSearchRespVO.NavVo();
                        navVo.setNavName(attr.getAttrName());
                        navVo.setNavValue(attrValue);
                        String string = "&attrs=" + attr.getAttrId() + "_" + attrValue;
                        if (productSearchParamVO.getQueryString().contains(string)) {
                            // 不在首位
                            productSearchParamVO.setQueryString(productSearchParamVO.getQueryString().replace(string, ""));
                            navVo.setLink("http://search.gulimall.com/search.html?" + productSearchParamVO.getQueryString());
                            navs.add(navVo);
                        } else if (productSearchParamVO.getQueryString().contains(string.replace("&", ""))) {
                            // 在首位
                            string = string.replace("&", "") + "&";
                            if (productSearchParamVO.getQueryString().contains(string)) {
                                // 证明后面还有参数  xxx&aaa - string xxx&
                                productSearchParamVO.setQueryString(productSearchParamVO.getQueryString().replace(string, ""));

                            } else {
                                // xxxx
                                string = string.replace("&", "");
                                productSearchParamVO.setQueryString(productSearchParamVO.getQueryString().replace(string, ""));
                            }
                            navVo.setLink("http://search.gulimall.com/search.html?" + productSearchParamVO.getQueryString());
                            navs.add(navVo);
                        }
                    }
                });
            });
            productSearchResp.setNavs(navs);
        }


        return productSearchResp;
    }

    private SearchSourceBuilder getSkuInfoBySearchDSL(ProductSearchParamVO productSearchParamVO) {
        // 构建查询条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 构建 bool 查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.构建must查询
        List<QueryBuilder> must = boolQuery.must();
        // 添加 key 查询条件
        if (StrUtil.isNotBlank(productSearchParamVO.getKeyword())) {
            must.add(QueryBuilders.matchQuery(FieldUtils.val(SkuEntity::getSkuTitle), productSearchParamVO.getKeyword()));
        }
        // 添加 catalogId 查询条件
        if (ObjectUtil.isNotEmpty(productSearchParamVO.getCatalog3Id())) {
            must.add(QueryBuilders.termQuery(FieldUtils.val(SkuEntity::getCatalogId), productSearchParamVO.getCatalog3Id()));
        }
        // 添加 brandId 多term匹配
        if (CollUtil.isNotEmpty(productSearchParamVO.getBrandId())) {
            must.add(QueryBuilders.termsQuery(FieldUtils.val(SkuEntity::getBrandId), productSearchParamVO.getBrandId()));
        }
        // 添加 attrs 嵌套查询
        if (CollUtil.isNotEmpty(productSearchParamVO.getAttrs())) {
            List<Integer> attrIds = productSearchParamVO.getAttrs().stream().map(attr -> {
                // TODO 需要做 9_xx_xxx_xx 格式校验
                return Integer.parseInt(attr.split("_")[0]);
            }).collect(Collectors.toList());
            TermsQueryBuilder termsNestQuery = QueryBuilders.termsQuery(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrId), attrIds);
            must.add(QueryBuilders.nestedQuery(FieldUtils.val(SkuEntity::getAttrs), termsNestQuery, ScoreMode.None));
        }

        // 2.构建filter查询
        List<QueryBuilder> filter = boolQuery.filter();
        // 添加 hasStock 查询条件
        if (ObjectUtil.isNotEmpty(productSearchParamVO.getHasStock())) {
            filter.add(QueryBuilders.termQuery(FieldUtils.val(SkuEntity::getHasStock), productSearchParamVO.getHasStock() == 1));
        }
        // 添加 skuPrice 区间查询条件
        if (ObjectUtil.isNotEmpty(productSearchParamVO.getSkuPrice())) {
            // TODO 需要做 0_500/_300/500_ 格式校验
            RangeQueryBuilder rangeSkuPriceQuery = QueryBuilders.rangeQuery(FieldUtils.val(SkuEntity::getSkuPrice));

            if (productSearchParamVO.getSkuPrice().contains("_")) {
                // 格式 正确
                if (productSearchParamVO.getSkuPrice().startsWith("_")) {
                    rangeSkuPriceQuery.lte(Float.valueOf(productSearchParamVO.getSkuPrice().replace("_", "")));
                } else if (productSearchParamVO.getSkuPrice().endsWith("_")) {
                    rangeSkuPriceQuery.gte(Float.valueOf(productSearchParamVO.getSkuPrice().replace("_", "")));
                } else {
                    rangeSkuPriceQuery.gte(Float.valueOf(productSearchParamVO.getSkuPrice().split("_")[0]))
                            .lte(Float.valueOf(productSearchParamVO.getSkuPrice().split("_")[1]));
                }

            }
            filter.add(rangeSkuPriceQuery);
        }
        // 添加 属性查询条件 - 一个属性id下有多个属性值
        if (CollUtil.isNotEmpty(productSearchParamVO.getAttrs())) {
            //TODO 需要做格式校验 - 9_xx:xxx:xx
            productSearchParamVO.getAttrs().forEach(attr -> {
                // 获取属性id
                BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
                int attrId = Integer.parseInt(attr.split("_")[0]);
                List<String> attrValues = Arrays.asList(attr.split("_")[1].split(":"));
                TermQueryBuilder termAttrIdQuery = QueryBuilders.termQuery(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrId), attrId);
                TermsQueryBuilder termsAttrValuesQueryBuilder = QueryBuilders.termsQuery(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrValue), attrValues);
                boolQueryBuilder.filter(termAttrIdQuery).filter(termsAttrValuesQueryBuilder);
                filter.add(QueryBuilders.nestedQuery(FieldUtils.val(SkuEntity::getAttrs), boolQueryBuilder, ScoreMode.None));
            });
        }

        // 添加 排序规则
        //TODO 需要做格式校验 - skuPrice:desc
        if (StrUtil.isNotBlank(productSearchParamVO.getSort())) {
            String[] sort = productSearchParamVO.getSort().split("_");
            sourceBuilder.sort(sort[0], SortOrder.fromString(sort[1]));
        }

        // 添加 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(FieldUtils.val(SkuEntity::getSkuTitle));
        highlightBuilder.preTags("<b style='color:red'>");
        highlightBuilder.postTags("</b>");
        sourceBuilder.highlighter(highlightBuilder);

        // 聚合查询
        // 构建品牌聚合查询
        TermsAggregationBuilder termsBrandNameAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity::getBrandName)).field(FieldUtils.val(SkuEntity::getBrandName));
        TermsAggregationBuilder termsBrandImgAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity::getBrandImg)).field(FieldUtils.val(SkuEntity::getBrandImg));
        AggregationBuilder termsBrandAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity::getBrandId)).field(FieldUtils.val(SkuEntity::getBrandId)).subAggregation(termsBrandNameAggregationBuilder).subAggregation(termsBrandImgAggregationBuilder);
        // 构建分类聚合查询
        TermsAggregationBuilder termsCatalogNameAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity::getCatalogName)).field(FieldUtils.val(SkuEntity::getCatalogName));
        TermsAggregationBuilder termsCatalogAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity::getCatalogId)).field(FieldUtils.val(SkuEntity::getCatalogId)).subAggregation(termsCatalogNameAggregationBuilder);
        // 构建内部属性聚合查询
        TermsAggregationBuilder termsAttrNameAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity.Attr::getAttrName)).field(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrName));
        TermsAggregationBuilder termsAttrValuesAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity.Attr::getAttrValue)).field(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrValue));
        TermsAggregationBuilder termsAttrIdAggregationBuilder = AggregationBuilders.terms(FieldUtils.val(SkuEntity.Attr::getAttrId)).field(FieldUtils.val(SkuEntity::getAttrs) + "." + FieldUtils.val(SkuEntity.Attr::getAttrId)).subAggregation(termsAttrNameAggregationBuilder).subAggregation(termsAttrValuesAggregationBuilder);
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nest_" + FieldUtils.val(SkuEntity::getAttrs), FieldUtils.val(SkuEntity::getAttrs)).subAggregation(termsAttrIdAggregationBuilder);
        // 添加 聚合
        sourceBuilder.aggregation(termsBrandAggregationBuilder);
        // 构建兄弟聚合
        AggregatorFactories.Builder builder = sourceBuilder.aggregations();
        // 添加 分类聚合
        builder.addAggregator(termsCatalogAggregationBuilder);
        // 添加 属性聚合
        builder.addAggregator(nestedAggregationBuilder);

        // 添加 分页
        // TODO 分页大小
        if (ObjectUtil.isNotEmpty(productSearchParamVO.getPageNum()) && productSearchParamVO.getPageNum() > 0) {
            sourceBuilder.from((productSearchParamVO.getPageNum() - 1) * 12);
            sourceBuilder.size(12);
        }

        sourceBuilder.query(boolQuery);

        return sourceBuilder;
    }

    @SneakyThrows
    private ProductSearchRespVO getProductSearchResp(SearchResponse response) {
        ProductSearchRespVO searchRespVO = new ProductSearchRespVO();

        // 获取聚合结果
        Aggregations aggregations = response.getAggregations();
        BeanUtil.copyProperties(parseAggregations(aggregations), searchRespVO);

        // 文档集合
        List<SkuEsRespVO> product = new LinkedList<>();
        // 获取文档 - hits
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            // 获取文档 - source
            SkuEsRespVO skuEntity = objectMapper.readValue(hit.getSourceAsString(), SkuEsRespVO.class);
            if (!hit.getHighlightFields().isEmpty()) {
                // 获取高亮
                String highlightSkuTitle = hit.getHighlightFields().get(FieldUtils.val(SkuEntity::getSkuTitle)).getFragments()[0].string();
                // 设置高亮
                skuEntity.setHighlightSkuTitle(highlightSkuTitle);
            }
            // 添加进文档集合
            product.add(skuEntity);
        }
        searchRespVO.setProduct(product);

        // 设置分页信息 - 总记录数
        searchRespVO.setTotal(response.getHits().getTotalHits().value);

        return searchRespVO;
    }


    public ProductSearchRespVO parseAggregations(Aggregations aggregations) {
        ProductSearchRespVO searchRespVO = new ProductSearchRespVO();

        // 非空校验
        if (aggregations == null || aggregations.asList().isEmpty()) {
            return searchRespVO;
        }

        for (Aggregation aggregation : aggregations) {
            // 得到聚合的名字
            String aggName = aggregation.getName();


            // 嵌套的聚合
            if (aggregation instanceof ParsedLongTerms) {
                ParsedLongTerms parsedLongTerms = (ParsedLongTerms) aggregation;

                if (FieldUtils.val(SkuEntity::getBrandId).equals(parsedLongTerms.getName())) {
                    // 品牌Id 的聚合结果
                    List<ProductSearchRespVO.BrandVo> brandVos = parsedLongTerms.getBuckets().stream().map(bucket -> {
                        ProductSearchRespVO.BrandVo brandVo = new ProductSearchRespVO.BrandVo();
                        brandVo.setBrandId(Long.parseLong(bucket.getKeyAsString()));
                        if (bucket.getAggregations() != null) {
                            if (bucket.getAggregations().getAsMap().get(FieldUtils.val(SkuEntity::getBrandName)) instanceof ParsedStringTerms) {
                                ParsedStringTerms parsedLongTermsBrandName = bucket.getAggregations().get(FieldUtils.val(SkuEntity::getBrandName));
                                brandVo.setBrandName(parsedLongTermsBrandName.getBuckets().get(0).getKeyAsString());
                            }
                            if (bucket.getAggregations().getAsMap().get(FieldUtils.val(SkuEntity::getBrandImg)) instanceof ParsedStringTerms) {
                                ParsedStringTerms parsedLongTermsBrandName = bucket.getAggregations().get(FieldUtils.val(SkuEntity::getBrandImg));
                                brandVo.setBrandImg(parsedLongTermsBrandName.getBuckets().get(0).getKeyAsString());
                            }
                        }
                        return brandVo;
                    }).collect(Collectors.toList());
                    searchRespVO.setBrands(brandVos);
                } else if (FieldUtils.val(SkuEntity::getCatalogId).equals(parsedLongTerms.getName())) {
                    // 分类Id 的聚合结果
                    List<ProductSearchRespVO.CatalogVo> catalogVos = parsedLongTerms.getBuckets().stream().map(bucket -> {
                        ProductSearchRespVO.CatalogVo catalogVo = new ProductSearchRespVO.CatalogVo();
                        catalogVo.setCatalogId(Long.parseLong(bucket.getKeyAsString()));
                        if (bucket.getAggregations() != null) {
                            if (bucket.getAggregations().getAsMap().get(FieldUtils.val(SkuEntity::getCatalogName)) instanceof ParsedStringTerms) {
                                ParsedStringTerms parsedLongTermsCatalogName = bucket.getAggregations().get(FieldUtils.val(SkuEntity::getCatalogName));
                                catalogVo.setCatalogName(parsedLongTermsCatalogName.getBuckets().get(0).getKeyAsString());
                            }
                        }
                        return catalogVo;
                    }).collect(Collectors.toList());
                    searchRespVO.setCatalogs(catalogVos);
                }

            } else if (aggregation instanceof ParsedNested) {
                ParsedNested parsedNested = (ParsedNested) aggregation;

                Aggregations nestedAggregations = parsedNested.getAggregations();
                if (nestedAggregations != null && !nestedAggregations.asList().isEmpty()) {
                    for (Aggregation nestedAggregation : nestedAggregations) {
                        // 嵌套的聚合
                        if (nestedAggregation instanceof ParsedLongTerms) {
                            ParsedLongTerms parsedLongTerms = (ParsedLongTerms) nestedAggregation;

                            if (FieldUtils.val(SkuEntity.Attr::getAttrId).equals(parsedLongTerms.getName())) {
                                // 属性Id 的聚合结果
                                List<ProductSearchRespVO.AttrVo> attrVos = parsedLongTerms.getBuckets().stream().map(bucket -> {
                                    ProductSearchRespVO.AttrVo attrVo = new ProductSearchRespVO.AttrVo();
                                    attrVo.setAttrId(Long.parseLong(bucket.getKeyAsString()));
                                    if (bucket.getAggregations() != null) {
                                        if (bucket.getAggregations().getAsMap().get(FieldUtils.val(SkuEntity.Attr::getAttrName)) instanceof ParsedStringTerms) {
                                            ParsedStringTerms parsedLongTermsBrandName = bucket.getAggregations().get(FieldUtils.val(SkuEntity.Attr::getAttrName));
                                            attrVo.setAttrName(parsedLongTermsBrandName.getBuckets().get(0).getKeyAsString());
                                        }
                                        if (bucket.getAggregations().getAsMap().get(FieldUtils.val(SkuEntity.Attr::getAttrValue)) instanceof ParsedStringTerms) {
                                            ParsedStringTerms parsedLongTermsBrandName = bucket.getAggregations().get(FieldUtils.val(SkuEntity.Attr::getAttrValue));
                                            List<String> values = parsedLongTermsBrandName.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());
                                            attrVo.setAttrValue(values);
                                        }
                                    }
                                    return attrVo;
                                }).collect(Collectors.toList());
                                searchRespVO.setAttrs(attrVos);
                            }
                        }
                    }
                }
            }
        }

        return searchRespVO;
    }
}
